Attempting to use tidymodels for processing our IBM krill data and
selecting variables of importance for each swimming parameter
library(tidymodels) # for the parsnip package, along with the rest of tidymodels
# Helper packages
library(readr) # for importing data
library(broom.mixed) # for converting bayesian models to tidy tibbles
library(dotwhisker) # for visualizing regression results
library(vip) # for variable importance plots
rm(list=ls(all=TRUE)) ## removes the previous workspace and environment so that we only have the data we need loaded in the session
load("~/Post-doc/krill-tank-code/DataProcessing/Notebooks/ParameterizationModel.Rdata")
dim(parameters)
glimpse(parameters)
dim(conditions)
glimpse(conditions)
df <- data.frame(conditions[,1],conditions[,2],conditions[,3],conditions[,4],parameters[,1], parameters[,7])
colnames(df) <- c("flow","chl", "guano", "light","ave.v", "dip.test")
df
df<-na.omit(df)
ave.v ~ flow * chl * guano * light
linear_reg()
linear_reg() %>%
set_engine("keras")
lm_mod <- linear_reg()
lm_fit <-
lm_mod %>%
fit(ave.v ~ flow * chl * guano * light, data = df) ## creates a linear model (LM)
lm_fit
tidy(lm_fit) ## summary of model
tidy(lm_fit) %>%
dwplot(dot_args = list(size = 2, color = "black"),
whisker_args = list(color = "black"),
vline = geom_vline(xintercept = 0, colour = "grey50", linetype = 2))
new_points <- expand.grid(flow = 6.1,
chl = 19,
guano = 0,
light = 0) ## create new points
new_points
mean_pred <- predict(lm_fit, new_data = new_points) ## mean predicted body width
mean_pred
conf_int_pred <- predict(lm_fit,
new_data = new_points,
type = "conf_int") ## confidence interval prediction
conf_int_pred
plot_data <-
new_points %>%
bind_cols(mean_pred) %>%
bind_cols(conf_int_pred)
# and plot:
ggplot(plot_data, aes(x = flow)) +
geom_point(aes(y = .pred)) +
geom_errorbar(aes(ymin = .pred_lower,
ymax = .pred_upper),
width = .2) +
labs(y = "ave v")
# set the prior distribution
prior_dist <- rstanarm::student_t(df = 1)
set.seed(123)
# make the parsnip model
bayes_mod <-
linear_reg() %>%
set_engine("stan",
prior_intercept = prior_dist,
prior = prior_dist)
# train the model
bayes_fit <-
bayes_mod %>%
fit(ave.v ~ flow * chl * guano * light, data = df)
print(bayes_fit, digits = 5)
tidy(bayes_fit, conf.int = TRUE)
bayes_plot_data <-
new_points %>%
bind_cols(predict(bayes_fit, new_data = new_points)) %>%
bind_cols(predict(bayes_fit, new_data = new_points, type = "conf_int"))
ggplot(bayes_plot_data, aes(x = flow)) +
geom_point(aes(y = .pred)) +
geom_errorbar(aes(ymin = .pred_lower, ymax = .pred_upper), width = .2) +
labs(y = "ave.v") +
ggtitle("Bayesian model with t(1) prior distribution")
### Tuning the model in sections
df %>%
group_by(light) %>%
summarize(med_flow = median(flow))
bayes_mod %>%
fit(ave.v ~ flow * chl * guano * light, data = df)
ggplot(df,
aes(flow, ave.v)) + # returns a ggplot object
geom_jitter() + # same
geom_smooth(method = lm, se = FALSE) + # same
labs(x = "flow", y = "ave.v") # etc
Inputs
rf_mod <- ## creates random forest model
rand_forest(trees = 1000) %>%
set_engine("ranger") %>%
set_mode("regression")
set.seed(234) ## comment out to get random runs
rf_fit <- ## fits random forest model to whole dataset
rf_mod %>%
fit(ave.v ~ flow * chl * guano * light, data = df)
rf_fit
rf_pred <- predict(rf_fit, df)
plot(df$ave.v, rf_pred$.pred, main = "corr - 0.7294325") ## observed vs predicted
cor <- cor.test(df$ave.v, rf_pred$.pred) ## gives correlation coef
rf_split <- initial_split(df %>% select(flow, chl, guano, light, ave.v), ## splits cases base on initial results
strata = NULL) ## with strata = NULL splits 11/31, rf_test has all guano absent, 50/50 light split, uneven flow split (0, 3, 3, 5.9, 5.9, 5.9, 8.9 x5), and random chl values.
## with strata = flow, splits 12/30, rf_test has 1 guano present, 50/50 light split, even flow splits (3x 0, 3, 4x 5.9 and 2x 8.9), and more even chl values.
## play with prop = 0.8, 0.9, etc
rf_train <- training(rf_split) ##creates training and testing datasets
rf_test <- testing(rf_split)
## comparisons to test data using ROC and accuracy to measure performance
rf_fit2 <- ## fits random forest model to training dataset
rf_mod %>%
fit(ave.v ~ flow * chl * guano * light, data = rf_train)
rf_fit2
rf_pred2 <- predict(rf_fit2, rf_test) ## compares to test data
plot(rf_test$ave.v, rf_pred2$.pred, main = "corr - 0.2700745") ## observed vs predicted, can add cor value from cor.test below
cor2 <- cor.test(rf_test$ave.v, rf_pred2$.pred) ## gives correlation coef
cor2$estimate
### can run with different seeds and splits, strata = NULL
#### loop and run 10 times with diff seeds
## look at consistency of results
Models
##### Example models
library(tidymodels) # for the parsnip package, along with the rest of tidymodels
# Helper packages
library(readr) # for importing data
library(broom.mixed) # for converting bayesian models to tidy tibbles
library(dotwhisker) # for visualizing regression results
library(vip) # for variable importance plots
rm(list=ls(all=TRUE)) ## removes the previous workspace and environment so that we only have the data we need loaded in the session
load("~/Bigelow/Data/ParameterizationModel.15.07.24.Rdata")
seeing if this fixes error
dim(parameters)
glimpse(parameters)
dim(conditions)
glimpse(conditions)
df <- data.frame(conditions[,1],conditions[,2],conditions[,3],conditions[,4],parameters[,12])
colnames(df) <- c("flow","chl", "guano", "light",colnames(parameters)[12])
df
df<-na.omit(df)
source("notebook16-functions.R")
## fix to pull col name or number into model??
colnames(df) <- c("flow","chl", "guano", "light", colnames(parameters)[12])
c.v <- NULL
c.v_train <- NULL
p <- NULL
rsq2 <- NULL
mod1 <- NULL
mod2 <- NULL
mod3 <- NULL
node.purity <- data.frame(matrix (NA, nrow = 10, ncol = 5))
colnames(node.purity) <- c( "c.e", "c.e.t", "r.squared", "p-value", "node purity")
library(ggplot2)
library(GGally)
library(hexbin)
library(diptest)
library(randomForest)
library(reprtree)
## loop through different swimming parameters contain this loop within the bigger loop
colnames(parameters)
for (j in colnames(parameters)){
df <- data.frame(conditions[,1],conditions[,2],conditions[,3],conditions[,4], parameters[,j])
colnames(df) <- c("flow","chl", "guano", "light", j)
df
df<-na.omit(df)
source("notebook16-functions.R")
print(colnames(df))
for (i in 1:10){
c.e <- rf.skill(df = df, trees = 2000, col1 = colnames(df)[5])
c.v <- rbind(c.e, c.v)
c.e.t <- rf.skill.test(df = df, trees = 2000, col1 = colnames(df)[5], prop = 0.8, strata = NULL, do.plot = FALSE) ## training data
c.v_train <- rbind(c.e.t, c.v_train)
rsq1 <- rf.fit(df = df, trees = 2000, col1 = colnames(df)[5])
rsq2 <- rbind(rsq1, rsq2)
output.test <- model.test(df = df, trees = 2000, col1 = colnames(df)[5], prop = 0.8, strata = NULL, do.plot = TRUE)
## output node purity
## create function to filter edge effect out (.5 cm out, dist to edge)
##ggsave(filename=paste('~/Bigelow/Figures/tidymodels Output/ave v/', i, '.tiff', sep = ''), plot = p, width = 24 , height = 12)
}
mod1 <- rbind(mod1, c.v) ## add identifier for each parameter into data frame
mod2 <- rbind(mod2, c.v_train)
mod3 <- rbind(mod3, rsq2)
## input average of p value, ce, cv, node purity and rsq into each column and row in matrix for each parameter and model type
## rank on best fit
## table for report of stats on models
}
## take best model for each pararmeter into simulation
## try vel mean and sd, and turn mean and sd
rf_pred_train <- predict(rf_fit_train, rf_test) ## need to predict each parameter within simulation
## environmental input to swimming, eg. node purity
c.v <- as.data.frame(c.v)
c.v_train <- as.data.frame(c.v_train)
rsq2 <- as.data.frame(rsq2)
node.purity$c.e <- c.v$V1
node.purity$c.e.t <- c.v_train$cor
node.purity$r.squared <- rsq2$V1
Node Purity heat map
### to save each plot as an individual graph
#jpeg(filename= paste('~/Bigelow/Figures/tidymodels Output/fit', i,'.jpeg', sep = ''), width = 960, height = 780)
#plot(df$response, rf_pred$.pred, main = paste("corr = ", cor$estimate)) ## observed vs predicted
#dev.off()
#best_tree <- rf.skill.test(df = df, trees = 2000, "ave.v", prop = 0.5, strata = NULL, do.plot = TRUE) ## not working....
#bt <- rbind(best_tree, bt)
## look at things outside random forest
rf_training_pred <-
predict(rf_fit, rf_train) %>%
bind_cols(predict(rf_fit, rf_train, type = "numeric")) %>%
# Add the true outcome data back in
bind_cols(rf_train %>%
select(flow, chl, guano, light))
rf_training_pred$light <- as.factor(rf_training_pred$light)
rf_training_pred$guano <- as.factor(rf_training_pred$guano)
rf_training_pred %>% # training set predictions
roc_auc(truth = light, .pred...1)
rf_training_pred %>% # training set predictions, only works for factors
accuracy(truth = ave.v, .pred...2)
## now that the model has exceptional performance lets move to the test dataset
rf_testing_pred <-
predict(rf_fit, rf_test) %>%
bind_cols(predict(rf_fit, rf_test, type = "numeric")) %>%
bind_cols(rf_test %>% select(flow, chl, guano, light))
rf_testing_pred$light <- as.factor(rf_testing_pred$light)
rf_testing_pred$guano <- as.factor(rf_testing_pred$guano)
rf_testing_pred %>% # test set predictions
roc_auc(truth = light, .pred...1)
rf_testing_pred %>% # test set predictions
accuracy(truth = light, guano)
## differences caused by training set error (bias) by model
###### resampling to the rescue
set.seed(345)
folds <- vfold_cv(rf_train, v = 10)
folds
rf_wf <- ## bundles workflow and random forest model together without a recipe needed
workflow() %>%
add_model(rf_mod) %>%
add_formula(ave.v ~ .)
set.seed(456)
rf_fit_rs <-
rf_wf %>%
fit_resamples(folds) ##fits resamples
rf_fit_rs ## .metrics column contains metrics on model performance
collect_metrics(rf_fit_rs) ##manually unnests meterics data
rf_testing_pred %>% # test set predictions (AS ABOVE)
roc_auc(truth = light, .pred...1)
rf_testing_pred %>% # test set predictions (AS ABOVE)
accuracy(truth = light, guano)
Tuning the model
library(glmnet)
library(rpart.plot) # for visualizing a decision tree
library(vip) # for variable importance plots
tune_spec <-
decision_tree( ## this is the type of model
cost_complexity = tune(),
tree_depth = tune()
) %>%
set_engine("rpart") %>%
set_mode("regression")
tune_spec
tree_grid <- grid_regular(cost_complexity(),
tree_depth(),
levels = 5)
tree_grid
tree_grid %>% ## shows each level we will tune the model at
count(tree_depth)
set.seed(234) ## don't understand what these do??
rf_folds <- vfold_cv(rf_train) ## creates cross-validation folds for tuning
set.seed(345)
tree_wf <- workflow() %>% ##creates the workflow
add_model(tune_spec) %>%
add_formula(ave.v ~ .)
tree_res <- ## resamples and tunes model
tree_wf %>%
tune_grid(
resamples = rf_folds,
grid = tree_grid
)
tree_res ## gives tuning results
tree_res %>%
collect_metrics() ## collects metrics from tuned models
tree_res %>%
collect_metrics() %>%
mutate(tree_depth = factor(tree_depth)) %>%
ggplot(aes(cost_complexity, mean, color = tree_depth)) +
geom_line(size = 1.5, alpha = 0.6) +
geom_point(size = 2) +
facet_wrap(~ .metric, scales = "free", nrow = 2) +
scale_x_log10(labels = scales::label_number()) +
scale_color_viridis_d(option = "plasma", begin = .9, end = 0)
## stubbiest tree with a depth of 1 performed the worst
## deepest tree with depth of 15 did better
tree_res %>%
show_best(metric = "rmse") ## shows best model fit
best_tree <- tree_res %>% ## pulls out data on the best fit
select_best(metric = "rmse")
best_tree ## summary of best tree model
final_wf <- ## create workflow from best tree model after tuning
tree_wf %>%
finalize_workflow(best_tree)
final_wf
final_fit <- ## create final model from new fit
final_wf %>%
last_fit(rf_split)
final_fit %>%
collect_metrics()
final_fit %>% ## plot ROC and compare performance after tuning
collect_predictions() %>%
roc_curve(flow, ave.v) %>% ### NOT WORKING
autoplot()
final_tree <- extract_workflow(final_fit) ## extract our final fit for future use
final_tree
final_tree %>% ## creates workflow plot
extract_fit_engine() %>%
rpart.plot(roundint = FALSE)
final_tree %>% ## shows which variables are most important to the model in a plot
extract_fit_parsnip() %>%
vip()
args(decision_tree)
Bigger RF Model
cores <- parallel::detectCores() ## sees how many cores we have to process the data
cores
rf_mod <- ## random forest model generation, parallel processing of models
rand_forest(mtry = tune(), min_n = tune(), trees = 1000) %>%
set_engine("ranger", num.threads = cores) %>%
set_mode("regression")
rf_recipe <- ## create random forest model recipe
recipe(ave.v ~ ., data = df)
rf_workflow <- ## create random forest model workflow
workflow() %>%
add_model(rf_mod) %>%
add_recipe(rf_recipe)
rf_mod
val_set <- validation_split(df,
strata = flow,
prop = 0.80)
val_set
# show what will be tuned
extract_parameter_set_dials(rf_mod)
set.seed(345)
rf_res <-
rf_workflow %>%
tune_grid(val_set,
grid = 25,
control = control_grid(save_pred = TRUE),
metrics = metric_set(rmse))
rf_res %>% ## shows 5 best random forest models out of the 25 candidates
show_best(metric = "rmse")
autoplot(rf_res) ## plot results
rf_best <- ## creates model with best predictors
rf_res %>%
select_best(metric = "rmse")
rf_best
rf_res %>% ## collects data for ROC curve plot
collect_predictions()
rf_best$mtry <- as.integer(rf_best$mtry)
##NOT WORKING
rf_auc <- ## creates set of models with best model and model model for comparison
rf_res %>%
collect_predictions(parameters = rf_best) %>%
roc_curve(mtry, .pred) %>%
mutate(model = "Random Forest")
##NOT WORKING
bind_rows(rf_best, rf_res) %>% ## plots model comparisons on ROC curve
ggplot(aes(x = 1 - specificity, y = sensitivity, col = model)) +
geom_path(lwd = 1.5, alpha = 0.8) +
geom_abline(lty = 3) +
coord_equal() +
scale_color_viridis_d(option = "plasma", end = .6)
################ last model after tuning
# the last model
last_rf_mod <-
rand_forest(mtry = 8, min_n = 7, trees = 1000) %>%
set_engine("ranger", num.threads = cores, importance = "impurity") %>%
set_mode("regression")
# the last workflow
last_rf_workflow <-
rf_workflow %>%
update_model(last_rf_mod)
# the last fit
set.seed(345)
last_rf_fit <-
last_rf_workflow %>%
last_fit(rf_split)
last_rf_fit
last_rf_fit %>% ## collect metrics from final model
collect_metrics()
last_rf_fit %>% ## updates model fit
extract_fit_parsnip() %>%
vip(num_features = 20)
##NOT WORKING
last_rf_fit %>% ## plots best ROC curve, with best set of hyperparameters as predictors
collect_predictions() %>%
roc_curve(ave.v, .pred...1) %>%
autoplot()
LS0tDQp0aXRsZTogIm5vdGVib29rMTYtdGlkeW1vZGVscyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQpBdHRlbXB0aW5nIHRvIHVzZSB0aWR5bW9kZWxzIGZvciBwcm9jZXNzaW5nIG91ciBJQk0ga3JpbGwgZGF0YSBhbmQgc2VsZWN0aW5nIHZhcmlhYmxlcyBvZiBpbXBvcnRhbmNlIGZvciBlYWNoIHN3aW1taW5nIHBhcmFtZXRlcg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeW1vZGVscykgICMgZm9yIHRoZSBwYXJzbmlwIHBhY2thZ2UsIGFsb25nIHdpdGggdGhlIHJlc3Qgb2YgdGlkeW1vZGVscw0KDQojIEhlbHBlciBwYWNrYWdlcw0KbGlicmFyeShyZWFkcikgICAgICAgIyBmb3IgaW1wb3J0aW5nIGRhdGENCmxpYnJhcnkoYnJvb20ubWl4ZWQpICMgZm9yIGNvbnZlcnRpbmcgYmF5ZXNpYW4gbW9kZWxzIHRvIHRpZHkgdGliYmxlcw0KbGlicmFyeShkb3R3aGlza2VyKSAgIyBmb3IgdmlzdWFsaXppbmcgcmVncmVzc2lvbiByZXN1bHRzDQpsaWJyYXJ5KHZpcCkgICAgICAgICAjIGZvciB2YXJpYWJsZSBpbXBvcnRhbmNlIHBsb3RzDQoNCg0Kcm0obGlzdD1scyhhbGw9VFJVRSkpICAgIyMgcmVtb3ZlcyB0aGUgcHJldmlvdXMgd29ya3NwYWNlIGFuZCBlbnZpcm9ubWVudCBzbyB0aGF0IHdlIG9ubHkgaGF2ZSB0aGUgZGF0YSB3ZSBuZWVkIGxvYWRlZCBpbiB0aGUgc2Vzc2lvbg0KbG9hZCgifi9Qb3N0LWRvYy9rcmlsbC10YW5rLWNvZGUvRGF0YVByb2Nlc3NpbmcvTm90ZWJvb2tzL1BhcmFtZXRlcml6YXRpb25Nb2RlbC5SZGF0YSIpDQoNCg0KZGltKHBhcmFtZXRlcnMpDQpnbGltcHNlKHBhcmFtZXRlcnMpDQoNCmRpbShjb25kaXRpb25zKQ0KZ2xpbXBzZShjb25kaXRpb25zKQ0KDQpkZiA8LSBkYXRhLmZyYW1lKGNvbmRpdGlvbnNbLDFdLGNvbmRpdGlvbnNbLDJdLGNvbmRpdGlvbnNbLDNdLGNvbmRpdGlvbnNbLDRdLHBhcmFtZXRlcnNbLDFdLCBwYXJhbWV0ZXJzWyw3XSkNCmNvbG5hbWVzKGRmKSA8LSBjKCJmbG93IiwiY2hsIiwgImd1YW5vIiwgImxpZ2h0IiwiYXZlLnYiLCAiZGlwLnRlc3QiKQ0KZGYNCmRmPC1uYS5vbWl0KGRmKQ0KDQoNCmF2ZS52IH4gZmxvdyAqIGNobCAqIGd1YW5vICogbGlnaHQNCg0KDQpsaW5lYXJfcmVnKCkNCg0KbGluZWFyX3JlZygpICU+JSANCiAgc2V0X2VuZ2luZSgia2VyYXMiKQ0KDQpsbV9tb2QgPC0gbGluZWFyX3JlZygpDQoNCmxtX2ZpdCA8LSANCiAgbG1fbW9kICU+JSANCiAgZml0KGF2ZS52IH4gZmxvdyAqIGNobCAqIGd1YW5vICogbGlnaHQsIGRhdGEgPSBkZikgICMjIGNyZWF0ZXMgYSBsaW5lYXIgbW9kZWwgKExNKQ0KbG1fZml0DQoNCnRpZHkobG1fZml0KSAgIyMgc3VtbWFyeSBvZiBtb2RlbA0KDQoNCnRpZHkobG1fZml0KSAlPiUgDQogIGR3cGxvdChkb3RfYXJncyA9IGxpc3Qoc2l6ZSA9IDIsIGNvbG9yID0gImJsYWNrIiksDQogICAgICAgICB3aGlza2VyX2FyZ3MgPSBsaXN0KGNvbG9yID0gImJsYWNrIiksDQogICAgICAgICB2bGluZSA9IGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGNvbG91ciA9ICJncmV5NTAiLCBsaW5ldHlwZSA9IDIpKQ0KDQpuZXdfcG9pbnRzIDwtIGV4cGFuZC5ncmlkKGZsb3cgPSA2LjEsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNobCA9IDE5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBndWFubyA9IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxpZ2h0ID0gMCkgIyMgY3JlYXRlIG5ldyBwb2ludHMNCm5ld19wb2ludHMNCg0KbWVhbl9wcmVkIDwtIHByZWRpY3QobG1fZml0LCBuZXdfZGF0YSA9IG5ld19wb2ludHMpICAjIyBtZWFuIHByZWRpY3RlZCBib2R5IHdpZHRoDQptZWFuX3ByZWQNCg0KY29uZl9pbnRfcHJlZCA8LSBwcmVkaWN0KGxtX2ZpdCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgbmV3X2RhdGEgPSBuZXdfcG9pbnRzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gImNvbmZfaW50IikgICMjIGNvbmZpZGVuY2UgaW50ZXJ2YWwgcHJlZGljdGlvbg0KY29uZl9pbnRfcHJlZA0KDQpwbG90X2RhdGEgPC0gDQogIG5ld19wb2ludHMgJT4lIA0KICBiaW5kX2NvbHMobWVhbl9wcmVkKSAlPiUgDQogIGJpbmRfY29scyhjb25mX2ludF9wcmVkKQ0KDQojIGFuZCBwbG90Og0KZ2dwbG90KHBsb3RfZGF0YSwgYWVzKHggPSBmbG93KSkgKyANCiAgZ2VvbV9wb2ludChhZXMoeSA9IC5wcmVkKSkgKyANCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IC5wcmVkX2xvd2VyLCANCiAgICAgICAgICAgICAgICAgICAgeW1heCA9IC5wcmVkX3VwcGVyKSwNCiAgICAgICAgICAgICAgICB3aWR0aCA9IC4yKSArIA0KICBsYWJzKHkgPSAiYXZlIHYiKQ0KDQojIHNldCB0aGUgcHJpb3IgZGlzdHJpYnV0aW9uDQpwcmlvcl9kaXN0IDwtIHJzdGFuYXJtOjpzdHVkZW50X3QoZGYgPSAxKQ0KDQpzZXQuc2VlZCgxMjMpDQoNCiMgbWFrZSB0aGUgcGFyc25pcCBtb2RlbA0KYmF5ZXNfbW9kIDwtICAgDQogIGxpbmVhcl9yZWcoKSAlPiUgDQogIHNldF9lbmdpbmUoInN0YW4iLCANCiAgICAgICAgICAgICBwcmlvcl9pbnRlcmNlcHQgPSBwcmlvcl9kaXN0LCANCiAgICAgICAgICAgICBwcmlvciA9IHByaW9yX2Rpc3QpIA0KDQojIHRyYWluIHRoZSBtb2RlbA0KYmF5ZXNfZml0IDwtIA0KICBiYXllc19tb2QgJT4lIA0KICBmaXQoYXZlLnYgfiBmbG93ICogY2hsICogZ3Vhbm8gKiBsaWdodCwgZGF0YSA9IGRmKQ0KDQpwcmludChiYXllc19maXQsIGRpZ2l0cyA9IDUpDQoNCnRpZHkoYmF5ZXNfZml0LCBjb25mLmludCA9IFRSVUUpDQoNCmJheWVzX3Bsb3RfZGF0YSA8LSANCiAgbmV3X3BvaW50cyAlPiUgDQogIGJpbmRfY29scyhwcmVkaWN0KGJheWVzX2ZpdCwgbmV3X2RhdGEgPSBuZXdfcG9pbnRzKSkgJT4lIA0KICBiaW5kX2NvbHMocHJlZGljdChiYXllc19maXQsIG5ld19kYXRhID0gbmV3X3BvaW50cywgdHlwZSA9ICJjb25mX2ludCIpKQ0KDQpnZ3Bsb3QoYmF5ZXNfcGxvdF9kYXRhLCBhZXMoeCA9IGZsb3cpKSArIA0KICBnZW9tX3BvaW50KGFlcyh5ID0gLnByZWQpKSArIA0KICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gLnByZWRfbG93ZXIsIHltYXggPSAucHJlZF91cHBlciksIHdpZHRoID0gLjIpICsgDQogIGxhYnMoeSA9ICJhdmUudiIpICsgDQogIGdndGl0bGUoIkJheWVzaWFuIG1vZGVsIHdpdGggdCgxKSBwcmlvciBkaXN0cmlidXRpb24iKQ0KDQojIyMgVHVuaW5nIHRoZSBtb2RlbCBpbiBzZWN0aW9ucw0KDQpkZiAlPiUgDQogIGdyb3VwX2J5KGxpZ2h0KSAlPiUgDQogIHN1bW1hcml6ZShtZWRfZmxvdyA9IG1lZGlhbihmbG93KSkNCg0KYmF5ZXNfbW9kICU+JSANCiAgZml0KGF2ZS52IH4gZmxvdyAqIGNobCAqIGd1YW5vICogbGlnaHQsIGRhdGEgPSBkZikNCg0KZ2dwbG90KGRmLA0KICAgICAgIGFlcyhmbG93LCBhdmUudikpICsgICAgICAjIHJldHVybnMgYSBnZ3Bsb3Qgb2JqZWN0IA0KICBnZW9tX2ppdHRlcigpICsgICAgICAgICAgICAgICAgICAgICAgICAgIyBzYW1lDQogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBzZSA9IEZBTFNFKSArICAjIHNhbWUgICAgICAgICAgICAgICAgICAgIA0KICBsYWJzKHggPSAiZmxvdyIsIHkgPSAiYXZlLnYiKSAgICAgICAgICMgZXRjDQpgYGANCklucHV0cw0KDQpgYGB7cn0NCg0KcmZfbW9kIDwtICMjIGNyZWF0ZXMgcmFuZG9tIGZvcmVzdCBtb2RlbA0KICByYW5kX2ZvcmVzdCh0cmVlcyA9IDEwMDApICU+JSANCiAgc2V0X2VuZ2luZSgicmFuZ2VyIikgJT4lIA0KICBzZXRfbW9kZSgicmVncmVzc2lvbiIpDQoNCg0Kc2V0LnNlZWQoMjM0KSAgIyMgY29tbWVudCBvdXQgdG8gZ2V0IHJhbmRvbSBydW5zDQoNCnJmX2ZpdCA8LSAjIyBmaXRzIHJhbmRvbSBmb3Jlc3QgbW9kZWwgdG8gd2hvbGUgZGF0YXNldA0KICByZl9tb2QgJT4lIA0KICBmaXQoYXZlLnYgfiBmbG93ICogY2hsICogZ3Vhbm8gKiBsaWdodCwgZGF0YSA9IGRmKQ0KcmZfZml0DQoNCnJmX3ByZWQgPC0gcHJlZGljdChyZl9maXQsIGRmKSAgDQpwbG90KGRmJGF2ZS52LCByZl9wcmVkJC5wcmVkLCBtYWluID0gImNvcnIgLSAwLjcyOTQzMjUiKSAgIyMgb2JzZXJ2ZWQgdnMgcHJlZGljdGVkDQpjb3IgPC0gY29yLnRlc3QoZGYkYXZlLnYsIHJmX3ByZWQkLnByZWQpICAjIyBnaXZlcyBjb3JyZWxhdGlvbiBjb2VmDQoNCnJmX3NwbGl0IDwtIGluaXRpYWxfc3BsaXQoZGYgJT4lIHNlbGVjdChmbG93LCBjaGwsIGd1YW5vLCBsaWdodCwgYXZlLnYpLCAjIyBzcGxpdHMgY2FzZXMgYmFzZSBvbiBpbml0aWFsIHJlc3VsdHMNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJhdGEgPSBOVUxMKSAgIyMgd2l0aCBzdHJhdGEgPSBOVUxMIHNwbGl0cyAxMS8zMSwgcmZfdGVzdCBoYXMgYWxsIGd1YW5vIGFic2VudCwgNTAvNTAgbGlnaHQgc3BsaXQsIHVuZXZlbiBmbG93IHNwbGl0ICgwLCAzLCAzLCA1LjksIDUuOSwgNS45LCA4LjkgeDUpLCBhbmQgcmFuZG9tIGNobCB2YWx1ZXMuDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMjIHdpdGggc3RyYXRhID0gZmxvdywgc3BsaXRzIDEyLzMwLCByZl90ZXN0IGhhcyAxIGd1YW5vIHByZXNlbnQsIDUwLzUwIGxpZ2h0IHNwbGl0LCBldmVuIGZsb3cgc3BsaXRzICgzeCAwLCAzLCA0eCA1LjkgYW5kIDJ4IDguOSksIGFuZCBtb3JlIGV2ZW4gY2hsIHZhbHVlcy4NCiMjIHBsYXkgd2l0aCBwcm9wID0gMC44LCAwLjksIGV0Yw0KDQoNCnJmX3RyYWluIDwtIHRyYWluaW5nKHJmX3NwbGl0KSAgIyNjcmVhdGVzIHRyYWluaW5nIGFuZCB0ZXN0aW5nIGRhdGFzZXRzDQpyZl90ZXN0ICA8LSB0ZXN0aW5nKHJmX3NwbGl0KQ0KDQojIyBjb21wYXJpc29ucyB0byB0ZXN0IGRhdGEgdXNpbmcgUk9DIGFuZCBhY2N1cmFjeSB0byBtZWFzdXJlIHBlcmZvcm1hbmNlDQoNCnJmX2ZpdDIgPC0gIyMgZml0cyByYW5kb20gZm9yZXN0IG1vZGVsIHRvIHRyYWluaW5nIGRhdGFzZXQNCiAgcmZfbW9kICU+JSANCiAgZml0KGF2ZS52IH4gZmxvdyAqIGNobCAqIGd1YW5vICogbGlnaHQsIGRhdGEgPSByZl90cmFpbikNCnJmX2ZpdDINCg0KcmZfcHJlZDIgPC0gcHJlZGljdChyZl9maXQyLCByZl90ZXN0KSAjIyBjb21wYXJlcyB0byB0ZXN0IGRhdGENCnBsb3QocmZfdGVzdCRhdmUudiwgcmZfcHJlZDIkLnByZWQsIG1haW4gPSAiY29yciAtIDAuMjcwMDc0NSIpICAjIyBvYnNlcnZlZCB2cyBwcmVkaWN0ZWQsIGNhbiBhZGQgY29yIHZhbHVlIGZyb20gY29yLnRlc3QgYmVsb3cNCmNvcjIgPC0gY29yLnRlc3QocmZfdGVzdCRhdmUudiwgcmZfcHJlZDIkLnByZWQpICAjIyBnaXZlcyBjb3JyZWxhdGlvbiBjb2VmDQpjb3IyJGVzdGltYXRlDQojIyMgY2FuIHJ1biB3aXRoIGRpZmZlcmVudCBzZWVkcyBhbmQgc3BsaXRzLCBzdHJhdGEgPSBOVUxMDQojIyMjIGxvb3AgYW5kIHJ1biAxMCB0aW1lcyB3aXRoIGRpZmYgc2VlZHMNCiMjIGxvb2sgYXQgY29uc2lzdGVuY3kgb2YgcmVzdWx0cw0KYGBgDQoNCk1vZGVscw0KYGBge3J9DQojIyMjIyBFeGFtcGxlIG1vZGVscw0KbGlicmFyeSh0aWR5bW9kZWxzKSAgIyBmb3IgdGhlIHBhcnNuaXAgcGFja2FnZSwgYWxvbmcgd2l0aCB0aGUgcmVzdCBvZiB0aWR5bW9kZWxzDQoNCiMgSGVscGVyIHBhY2thZ2VzDQpsaWJyYXJ5KHJlYWRyKSAgICAgICAjIGZvciBpbXBvcnRpbmcgZGF0YQ0KbGlicmFyeShicm9vbS5taXhlZCkgIyBmb3IgY29udmVydGluZyBiYXllc2lhbiBtb2RlbHMgdG8gdGlkeSB0aWJibGVzDQpsaWJyYXJ5KGRvdHdoaXNrZXIpICAjIGZvciB2aXN1YWxpemluZyByZWdyZXNzaW9uIHJlc3VsdHMNCmxpYnJhcnkodmlwKSAgICAgICAgICMgZm9yIHZhcmlhYmxlIGltcG9ydGFuY2UgcGxvdHMNCg0KDQpybShsaXN0PWxzKGFsbD1UUlVFKSkgICAjIyByZW1vdmVzIHRoZSBwcmV2aW91cyB3b3Jrc3BhY2UgYW5kIGVudmlyb25tZW50IHNvIHRoYXQgd2Ugb25seSBoYXZlIHRoZSBkYXRhIHdlIG5lZWQgbG9hZGVkIGluIHRoZSBzZXNzaW9uDQpsb2FkKCJ+L0JpZ2Vsb3cvRGF0YS9QYXJhbWV0ZXJpemF0aW9uTW9kZWwuMTUuMDcuMjQuUmRhdGEiKQ0KYGBgDQoNCnNlZWluZyBpZiB0aGlzIGZpeGVzIGVycm9yDQpgYGB7cn0NCmRpbShwYXJhbWV0ZXJzKQ0KZ2xpbXBzZShwYXJhbWV0ZXJzKQ0KDQpkaW0oY29uZGl0aW9ucykNCmdsaW1wc2UoY29uZGl0aW9ucykNCg0KZGYgPC0gZGF0YS5mcmFtZShjb25kaXRpb25zWywxXSxjb25kaXRpb25zWywyXSxjb25kaXRpb25zWywzXSxjb25kaXRpb25zWyw0XSxwYXJhbWV0ZXJzWywxMl0pDQpjb2xuYW1lcyhkZikgPC0gYygiZmxvdyIsImNobCIsICJndWFubyIsICJsaWdodCIsY29sbmFtZXMocGFyYW1ldGVycylbMTJdKQ0KZGYNCmRmPC1uYS5vbWl0KGRmKQ0KDQpzb3VyY2UoIm5vdGVib29rMTYtZnVuY3Rpb25zLlIiKQ0KIyMgZml4IHRvIHB1bGwgY29sIG5hbWUgb3IgbnVtYmVyIGludG8gbW9kZWw/Pw0KY29sbmFtZXMoZGYpIDwtIGMoImZsb3ciLCJjaGwiLCAiZ3Vhbm8iLCAibGlnaHQiLCBjb2xuYW1lcyhwYXJhbWV0ZXJzKVsxMl0pDQpjLnYgPC0gTlVMTA0KYy52X3RyYWluIDwtIE5VTEwNCnAgPC0gTlVMTA0KcnNxMiA8LSBOVUxMDQptb2QxIDwtIE5VTEwNCm1vZDIgPC0gTlVMTA0KbW9kMyA8LSBOVUxMDQpub2RlLnB1cml0eSA8LSBkYXRhLmZyYW1lKG1hdHJpeCAoTkEsIG5yb3cgPSAxMCwgbmNvbCA9IDUpKQ0KY29sbmFtZXMobm9kZS5wdXJpdHkpIDwtIGMoICJjLmUiLCAiYy5lLnQiLCAici5zcXVhcmVkIiwgInAtdmFsdWUiLCAibm9kZSBwdXJpdHkiKQ0KDQoNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoR0dhbGx5KQ0KbGlicmFyeShoZXhiaW4pDQpsaWJyYXJ5KGRpcHRlc3QpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmxpYnJhcnkocmVwcnRyZWUpDQoNCiMjIGxvb3AgdGhyb3VnaCBkaWZmZXJlbnQgc3dpbW1pbmcgcGFyYW1ldGVycyBjb250YWluIHRoaXMgbG9vcCB3aXRoaW4gdGhlIGJpZ2dlciBsb29wDQpjb2xuYW1lcyhwYXJhbWV0ZXJzKQ0KDQoNCmZvciAoaiBpbiBjb2xuYW1lcyhwYXJhbWV0ZXJzKSl7DQogICAgZGYgPC0gZGF0YS5mcmFtZShjb25kaXRpb25zWywxXSxjb25kaXRpb25zWywyXSxjb25kaXRpb25zWywzXSxjb25kaXRpb25zWyw0XSwgcGFyYW1ldGVyc1ssal0pDQogICAgY29sbmFtZXMoZGYpIDwtIGMoImZsb3ciLCJjaGwiLCAiZ3Vhbm8iLCAibGlnaHQiLCBqKQ0KICAgIGRmDQogICAgZGY8LW5hLm9taXQoZGYpDQogICAgc291cmNlKCJub3RlYm9vazE2LWZ1bmN0aW9ucy5SIikNCiAgICBwcmludChjb2xuYW1lcyhkZikpDQoNCiAgICAgIGZvciAoaSBpbiAxOjEwKXsgIA0KDQogICAgICAgIGMuZSA8LSByZi5za2lsbChkZiA9IGRmLCB0cmVlcyA9IDIwMDAsIGNvbDEgPSBjb2xuYW1lcyhkZilbNV0pIA0KICAgICAgICBjLnYgPC0gcmJpbmQoYy5lLCBjLnYpICANCiAgICAgICAgYy5lLnQgPC0gcmYuc2tpbGwudGVzdChkZiA9IGRmLCB0cmVlcyA9IDIwMDAsIGNvbDEgPSBjb2xuYW1lcyhkZilbNV0sIHByb3AgPSAwLjgsIHN0cmF0YSA9IE5VTEwsIGRvLnBsb3QgPSBGQUxTRSkgICAjIyB0cmFpbmluZyBkYXRhDQogICAgICAgIGMudl90cmFpbiA8LSByYmluZChjLmUudCwgYy52X3RyYWluKQ0KICAgICAgICByc3ExIDwtIHJmLmZpdChkZiA9IGRmLCB0cmVlcyA9IDIwMDAsIGNvbDEgPSBjb2xuYW1lcyhkZilbNV0pDQogICAgICAgIHJzcTIgPC0gcmJpbmQocnNxMSwgcnNxMikNCiAgDQogICAgICAgIG91dHB1dC50ZXN0IDwtIG1vZGVsLnRlc3QoZGYgPSBkZiwgdHJlZXMgPSAyMDAwLCBjb2wxID0gY29sbmFtZXMoZGYpWzVdLCBwcm9wID0gMC44LCBzdHJhdGEgPSBOVUxMLCBkby5wbG90ID0gVFJVRSkNCiAgICAgICAgDQogICAgICAgICMjIG91dHB1dCBub2RlIHB1cml0eQ0KICANCiAgICAgICAgIyMgY3JlYXRlIGZ1bmN0aW9uIHRvIGZpbHRlciBlZGdlIGVmZmVjdCBvdXQgKC41IGNtIG91dCwgZGlzdCB0byBlZGdlKQ0KICANCiAgICAgICAgIyNnZ3NhdmUoZmlsZW5hbWU9cGFzdGUoJ34vQmlnZWxvdy9GaWd1cmVzL3RpZHltb2RlbHMgT3V0cHV0L2F2ZSB2LycsIGksICcudGlmZicsIHNlcCA9ICcnKSwgcGxvdCA9IHAsICAgICB3aWR0aCA9IDI0ICwgaGVpZ2h0ID0gMTIpDQogICAgICB9DQogICAgbW9kMSA8LSByYmluZChtb2QxLCBjLnYpICAjIyBhZGQgaWRlbnRpZmllciBmb3IgZWFjaCBwYXJhbWV0ZXIgaW50byBkYXRhIGZyYW1lDQogICAgbW9kMiA8LSByYmluZChtb2QyLCBjLnZfdHJhaW4pDQogICAgbW9kMyA8LSByYmluZChtb2QzLCByc3EyKQ0KICAgIA0KICAgICMjIGlucHV0IGF2ZXJhZ2Ugb2YgcCB2YWx1ZSwgY2UsIGN2LCBub2RlIHB1cml0eSBhbmQgcnNxIGludG8gZWFjaCBjb2x1bW4gYW5kIHJvdyBpbiBtYXRyaXggZm9yIGVhY2ggcGFyYW1ldGVyIGFuZCBtb2RlbCB0eXBlDQogICAgDQogICAgIyMgcmFuayBvbiBiZXN0IGZpdA0KICAgICMjIHRhYmxlIGZvciByZXBvcnQgb2Ygc3RhdHMgb24gbW9kZWxzDQogICAgDQp9DQoNCiMjIHRha2UgYmVzdCBtb2RlbCBmb3IgZWFjaCBwYXJhcm1ldGVyIGludG8gc2ltdWxhdGlvbg0KIyMgdHJ5IHZlbCBtZWFuIGFuZCBzZCwgYW5kIHR1cm4gbWVhbiBhbmQgc2QgDQoNCnJmX3ByZWRfdHJhaW4gPC0gcHJlZGljdChyZl9maXRfdHJhaW4sIHJmX3Rlc3QpICMjIG5lZWQgdG8gcHJlZGljdCBlYWNoIHBhcmFtZXRlciB3aXRoaW4gc2ltdWxhdGlvbg0KDQojIyBlbnZpcm9ubWVudGFsIGlucHV0IHRvIHN3aW1taW5nLCBlZy4gbm9kZSBwdXJpdHkNCg0KDQoNCmMudiA8LSBhcy5kYXRhLmZyYW1lKGMudikNCmMudl90cmFpbiA8LSBhcy5kYXRhLmZyYW1lKGMudl90cmFpbikNCnJzcTIgPC0gYXMuZGF0YS5mcmFtZShyc3EyKQ0KDQpub2RlLnB1cml0eSRjLmUgPC0gYy52JFYxDQpub2RlLnB1cml0eSRjLmUudCA8LSBjLnZfdHJhaW4kY29yDQpub2RlLnB1cml0eSRyLnNxdWFyZWQgPC0gcnNxMiRWMQ0KDQpgYGANCg0KTm9kZSBQdXJpdHkgaGVhdCBtYXANCmBgYHtyfQ0KIyMjIyMgRXhhbXBsZSBkYXRhDQpzZXQuc2VlZCgxMjMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCmRhdGE8LSBtYXRyaXgocm5vcm0oMTAwLCAwLCAxMCksIG5yb3cgPSAxMCwgbmNvbCA9IDEwKSAgICAgICAgICAgIyBDcmVhdGUgZXhhbXBsZSBkYXRhICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBBcHBseSBoZWF0bWFwIGZ1bmN0aW9uDQoNCmNvbG5hbWVzKGRhdGEpPC0gcGFzdGUwKCJjb2wiLCAxOjEwKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBDb2x1bW4gbmFtZXMNCnJvd25hbWVzKGRhdGEpPC0gcGFzdGUwKCJyb3ciLCAxOjEwKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSb3cgbmFtZXMNCmhlYWQoZGF0YSw1KQ0KDQpub2RlIDwtIHJlYWQuY3N2KCJDOlxcVXNlcnNcXE5pY29sZSBIZWxsZXNzZXlcXERvY3VtZW50c1xcQmlnZWxvd1xcRGF0YVxcTm9kZSBQdXJpdHkgVmFsdWVzIC0gT3ZlcmFsbCBQcmVkaWN0b3JzLmNzdiIsIGhlYWRlciA9IFQpDQpoZWFkKG5vZGUpDQpub2RlMiA8LSBkYXRhLm1hdHJpeChub2RlKQ0KDQojIyMjI25vZGUjIyMjIyBFeGFtcGxlIDENCmhlYXRtYXAobm9kZTIpICANCg0KIyMjIyMgRXhhbXBsZSAyDQpoZWF0bWFwKG5vZGUyLCBSb3d2ID0gTkEsIENvbHYgPSBOQSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZW1vdmUgZGVuZG9ncmFtDQoNCiMjIyMjIEV4YW1wbGUgMw0KbXlfY29sb3JzPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJjeWFuIiwgImRlZXBwaW5rMyIpKSAgICAgICAgICAgICAjIE1hbnVhbCBjb2xvciByYW5nZQ0KaGVhdG1hcChub2RlMiwgY29sID0gbXlfY29sb3JzKDEwMCkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgSGVhdG1hcCB3aXRoIG1hbnVhbCBjb2xvcnMNCg0KIyMjIyMgRXhhbXBsZSA0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEluc3RhbGwgcmVzaGFwZSBwYWNrYWdlDQpsaWJyYXJ5KHJlc2hhcGUpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBMb2FkIHJlc2hhcGUgcGFja2FnZQ0KDQpub2RlX21lbHQgPC0gbWVsdChub2RlKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFJlb3JkZXIgZGF0YQ0KbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTG9hZCBnZ3Bsb3QyIHBhY2thZ2UNCg0KZ2dwIDwtIGdncGxvdChub2RlX21lbHQsIGFlcyhGYWN0b3IsIHZhcmlhYmxlKSkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICMgQ3JlYXRlIGhlYXRtYXAgd2l0aCBnZ3Bsb3QyDQogIGdlb21fdGlsZShhZXMoZmlsbCA9IHZhbHVlKSkNCmdncCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUHJpbnQgaGVhdG1hcA0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocmVzaGFwZTIpDQoNCmF0dGFjaChub2RlX21lbHQpDQoNCmdncGxvdChub2RlX21lbHQsIGFlcyh4ID0gRmFjdG9yLCB5ID0gdmFyaWFibGUsIGZpbGwgPSB2YWx1ZSkpICsNCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAicmVkIikgKw0KICBsYWJzKHggPSAiRW52aXJvbm1lbnRhbCBDb25kaXRpb24iLCB5ID0gIlN3aW1taW5nIFBhcmFtZXRlciIsIHRpdGxlID0gIk5vZGUgUHVyaXR5IEhlYXRtYXAgLSBPdmVyYWxsIFByZWRpY3RvcnMiKQ0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgbG9hZCByZXF1aXJlZCBwYWNrYWdlcw0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KDQojIGNyZWF0ZSBoZWF0bWFwIHVzaW5nIHBsb3RseQ0KcGxvdF9seShub2RlX21lbHQsIHggPSBGYWN0b3IsIHkgPSB2YXJpYWJsZSwgeiA9IHZhbHVlLCB0eXBlID0gImhlYXRtYXAiKSAlPiUNCiAgbGF5b3V0KHRpdGxlID0gIkhlYXRtYXAiLCB4YXhpcyA9IGxpc3QodGl0bGUgPSAiQ29sdW1uIiksIHlheGlzID0gbGlzdCh0aXRsZSA9ICJSb3ciKSkNCmBgYA0KDQoNCmBgYHtyfQ0KICAjIyMgdG8gc2F2ZSBlYWNoIHBsb3QgYXMgYW4gaW5kaXZpZHVhbCBncmFwaA0KICAjanBlZyhmaWxlbmFtZT0gcGFzdGUoJ34vQmlnZWxvdy9GaWd1cmVzL3RpZHltb2RlbHMgT3V0cHV0L2ZpdCcsIGksJy5qcGVnJywgc2VwID0gJycpLCB3aWR0aCA9IDk2MCwgICAgICBoZWlnaHQgPSA3ODApDQogICNwbG90KGRmJHJlc3BvbnNlLCByZl9wcmVkJC5wcmVkLCBtYWluID0gcGFzdGUoImNvcnIgPSAiLCBjb3IkZXN0aW1hdGUpKSAgIyMgb2JzZXJ2ZWQgdnMgcHJlZGljdGVkDQogICNkZXYub2ZmKCkNCg0KDQogICNiZXN0X3RyZWUgPC0gcmYuc2tpbGwudGVzdChkZiA9IGRmLCB0cmVlcyA9IDIwMDAsICJhdmUudiIsIHByb3AgPSAwLjUsIHN0cmF0YSA9IE5VTEwsIGRvLnBsb3QgPSBUUlVFKSAjIyBub3Qgd29ya2luZy4uLi4NCiAgI2J0IDwtIHJiaW5kKGJlc3RfdHJlZSwgYnQpDQoNCiMjIGxvb2sgYXQgdGhpbmdzIG91dHNpZGUgcmFuZG9tIGZvcmVzdA0KDQoNCnJmX3RyYWluaW5nX3ByZWQgPC0gDQogIHByZWRpY3QocmZfZml0LCByZl90cmFpbikgJT4lIA0KICBiaW5kX2NvbHMocHJlZGljdChyZl9maXQsIHJmX3RyYWluLCB0eXBlID0gIm51bWVyaWMiKSkgJT4lIA0KICAjIEFkZCB0aGUgdHJ1ZSBvdXRjb21lIGRhdGEgYmFjayBpbg0KICBiaW5kX2NvbHMocmZfdHJhaW4gJT4lIA0KICAgICAgICAgICAgICBzZWxlY3QoZmxvdywgY2hsLCBndWFubywgbGlnaHQpKQ0KDQpyZl90cmFpbmluZ19wcmVkJGxpZ2h0IDwtIGFzLmZhY3RvcihyZl90cmFpbmluZ19wcmVkJGxpZ2h0KQ0KcmZfdHJhaW5pbmdfcHJlZCRndWFubyA8LSBhcy5mYWN0b3IocmZfdHJhaW5pbmdfcHJlZCRndWFubykNCg0KcmZfdHJhaW5pbmdfcHJlZCAlPiUgICAgICAgICAgICAgICAgIyB0cmFpbmluZyBzZXQgcHJlZGljdGlvbnMNCiAgcm9jX2F1Yyh0cnV0aCA9IGxpZ2h0LCAucHJlZC4uLjEpDQoNCnJmX3RyYWluaW5nX3ByZWQgJT4lICAgICAgICAgICAgICAgICMgdHJhaW5pbmcgc2V0IHByZWRpY3Rpb25zLCBvbmx5IHdvcmtzIGZvciBmYWN0b3JzDQogIGFjY3VyYWN5KHRydXRoID0gYXZlLnYsIC5wcmVkLi4uMikNCg0KIyMgbm93IHRoYXQgdGhlIG1vZGVsIGhhcyBleGNlcHRpb25hbCBwZXJmb3JtYW5jZSBsZXRzIG1vdmUgdG8gdGhlIHRlc3QgZGF0YXNldA0KDQpyZl90ZXN0aW5nX3ByZWQgPC0gDQogIHByZWRpY3QocmZfZml0LCByZl90ZXN0KSAlPiUgDQogIGJpbmRfY29scyhwcmVkaWN0KHJmX2ZpdCwgcmZfdGVzdCwgdHlwZSA9ICJudW1lcmljIikpICU+JSANCiAgYmluZF9jb2xzKHJmX3Rlc3QgJT4lIHNlbGVjdChmbG93LCBjaGwsIGd1YW5vLCBsaWdodCkpDQoNCnJmX3Rlc3RpbmdfcHJlZCRsaWdodCA8LSBhcy5mYWN0b3IocmZfdGVzdGluZ19wcmVkJGxpZ2h0KQ0KcmZfdGVzdGluZ19wcmVkJGd1YW5vIDwtIGFzLmZhY3RvcihyZl90ZXN0aW5nX3ByZWQkZ3Vhbm8pDQoNCnJmX3Rlc3RpbmdfcHJlZCAlPiUgICAgICAgICAgICAgICAgICAgIyB0ZXN0IHNldCBwcmVkaWN0aW9ucw0KICByb2NfYXVjKHRydXRoID0gbGlnaHQsIC5wcmVkLi4uMSkNCg0KcmZfdGVzdGluZ19wcmVkICU+JSAgICAgICAgICAgICAgICAgICAjIHRlc3Qgc2V0IHByZWRpY3Rpb25zDQogIGFjY3VyYWN5KHRydXRoID0gbGlnaHQsIGd1YW5vKQ0KDQojIyBkaWZmZXJlbmNlcyBjYXVzZWQgYnkgdHJhaW5pbmcgc2V0IGVycm9yIChiaWFzKSBieSBtb2RlbA0KDQojIyMjIyMgcmVzYW1wbGluZyB0byB0aGUgcmVzY3VlDQoNCnNldC5zZWVkKDM0NSkNCmZvbGRzIDwtIHZmb2xkX2N2KHJmX3RyYWluLCB2ID0gMTApDQpmb2xkcw0KDQpyZl93ZiA8LSAjIyBidW5kbGVzIHdvcmtmbG93IGFuZCByYW5kb20gZm9yZXN0IG1vZGVsIHRvZ2V0aGVyIHdpdGhvdXQgYSByZWNpcGUgbmVlZGVkDQogIHdvcmtmbG93KCkgJT4lDQogIGFkZF9tb2RlbChyZl9tb2QpICU+JQ0KICBhZGRfZm9ybXVsYShhdmUudiB+IC4pDQoNCnNldC5zZWVkKDQ1NikNCnJmX2ZpdF9ycyA8LSANCiAgcmZfd2YgJT4lIA0KICBmaXRfcmVzYW1wbGVzKGZvbGRzKSAgIyNmaXRzIHJlc2FtcGxlcw0KDQpyZl9maXRfcnMgICMjIC5tZXRyaWNzIGNvbHVtbiBjb250YWlucyBtZXRyaWNzIG9uIG1vZGVsIHBlcmZvcm1hbmNlDQoNCmNvbGxlY3RfbWV0cmljcyhyZl9maXRfcnMpICAjI21hbnVhbGx5IHVubmVzdHMgbWV0ZXJpY3MgZGF0YQ0KDQpyZl90ZXN0aW5nX3ByZWQgJT4lICAgICAgICAgICAgICAgICAgICMgdGVzdCBzZXQgcHJlZGljdGlvbnMgKEFTIEFCT1ZFKQ0KICByb2NfYXVjKHRydXRoID0gbGlnaHQsIC5wcmVkLi4uMSkNCg0KcmZfdGVzdGluZ19wcmVkICU+JSAgICAgICAgICAgICAgICAgICAjIHRlc3Qgc2V0IHByZWRpY3Rpb25zICAoQVMgQUJPVkUpDQogIGFjY3VyYWN5KHRydXRoID0gbGlnaHQsIGd1YW5vKQ0KYGBgDQpUdW5pbmcgdGhlIG1vZGVsDQpgYGB7cn0NCmxpYnJhcnkoZ2xtbmV0KQ0KbGlicmFyeShycGFydC5wbG90KSAgIyBmb3IgdmlzdWFsaXppbmcgYSBkZWNpc2lvbiB0cmVlDQpsaWJyYXJ5KHZpcCkgICAgICAgICAjIGZvciB2YXJpYWJsZSBpbXBvcnRhbmNlIHBsb3RzDQoNCnR1bmVfc3BlYyA8LSANCiAgZGVjaXNpb25fdHJlZSggICMjIHRoaXMgaXMgdGhlIHR5cGUgb2YgbW9kZWwNCiAgICBjb3N0X2NvbXBsZXhpdHkgPSB0dW5lKCksDQogICAgdHJlZV9kZXB0aCA9IHR1bmUoKQ0KICApICU+JSANCiAgc2V0X2VuZ2luZSgicnBhcnQiKSAlPiUgDQogIHNldF9tb2RlKCJyZWdyZXNzaW9uIikNCg0KdHVuZV9zcGVjDQoNCnRyZWVfZ3JpZCA8LSBncmlkX3JlZ3VsYXIoY29zdF9jb21wbGV4aXR5KCksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRyZWVfZGVwdGgoKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gNSkNCg0KdHJlZV9ncmlkDQoNCnRyZWVfZ3JpZCAlPiUgIyMgc2hvd3MgZWFjaCBsZXZlbCB3ZSB3aWxsIHR1bmUgdGhlIG1vZGVsIGF0DQogIGNvdW50KHRyZWVfZGVwdGgpDQoNCnNldC5zZWVkKDIzNCkgIyMgZG9uJ3QgdW5kZXJzdGFuZCB3aGF0IHRoZXNlIGRvPz8NCnJmX2ZvbGRzIDwtIHZmb2xkX2N2KHJmX3RyYWluKSAgIyMgY3JlYXRlcyBjcm9zcy12YWxpZGF0aW9uIGZvbGRzIGZvciB0dW5pbmcNCg0Kc2V0LnNlZWQoMzQ1KQ0KDQp0cmVlX3dmIDwtIHdvcmtmbG93KCkgJT4lICAjI2NyZWF0ZXMgdGhlIHdvcmtmbG93DQogIGFkZF9tb2RlbCh0dW5lX3NwZWMpICU+JQ0KICBhZGRfZm9ybXVsYShhdmUudiB+IC4pDQoNCnRyZWVfcmVzIDwtICMjIHJlc2FtcGxlcyBhbmQgdHVuZXMgbW9kZWwNCiAgdHJlZV93ZiAlPiUgDQogIHR1bmVfZ3JpZCgNCiAgICByZXNhbXBsZXMgPSByZl9mb2xkcywNCiAgICBncmlkID0gdHJlZV9ncmlkDQogICAgKQ0KDQp0cmVlX3JlcyAgIyMgZ2l2ZXMgdHVuaW5nIHJlc3VsdHMNCg0KdHJlZV9yZXMgJT4lIA0KICBjb2xsZWN0X21ldHJpY3MoKSAgIyMgY29sbGVjdHMgbWV0cmljcyBmcm9tIHR1bmVkIG1vZGVscw0KDQoNCnRyZWVfcmVzICU+JQ0KICBjb2xsZWN0X21ldHJpY3MoKSAlPiUNCiAgbXV0YXRlKHRyZWVfZGVwdGggPSBmYWN0b3IodHJlZV9kZXB0aCkpICU+JQ0KICBnZ3Bsb3QoYWVzKGNvc3RfY29tcGxleGl0eSwgbWVhbiwgY29sb3IgPSB0cmVlX2RlcHRoKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuNSwgYWxwaGEgPSAwLjYpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMikgKw0KICBmYWNldF93cmFwKH4gLm1ldHJpYywgc2NhbGVzID0gImZyZWUiLCBucm93ID0gMikgKw0KICBzY2FsZV94X2xvZzEwKGxhYmVscyA9IHNjYWxlczo6bGFiZWxfbnVtYmVyKCkpICsNCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKG9wdGlvbiA9ICJwbGFzbWEiLCBiZWdpbiA9IC45LCBlbmQgPSAwKQ0KDQojIyBzdHViYmllc3QgdHJlZSB3aXRoIGEgZGVwdGggb2YgMSBwZXJmb3JtZWQgdGhlIHdvcnN0DQojIyBkZWVwZXN0IHRyZWUgd2l0aCBkZXB0aCBvZiAxNSBkaWQgYmV0dGVyDQoNCnRyZWVfcmVzICU+JQ0KICBzaG93X2Jlc3QobWV0cmljID0gInJtc2UiKSAgIyMgc2hvd3MgYmVzdCBtb2RlbCBmaXQNCg0KYmVzdF90cmVlIDwtIHRyZWVfcmVzICU+JSAgIyMgcHVsbHMgb3V0IGRhdGEgb24gdGhlIGJlc3QgZml0DQogIHNlbGVjdF9iZXN0KG1ldHJpYyA9ICJybXNlIikNCg0KYmVzdF90cmVlICMjIHN1bW1hcnkgb2YgYmVzdCB0cmVlIG1vZGVsDQoNCmZpbmFsX3dmIDwtICMjIGNyZWF0ZSB3b3JrZmxvdyBmcm9tIGJlc3QgdHJlZSBtb2RlbCBhZnRlciB0dW5pbmcNCiAgdHJlZV93ZiAlPiUgDQogIGZpbmFsaXplX3dvcmtmbG93KGJlc3RfdHJlZSkNCg0KZmluYWxfd2YNCg0KZmluYWxfZml0IDwtICMjIGNyZWF0ZSBmaW5hbCBtb2RlbCBmcm9tIG5ldyBmaXQNCiAgZmluYWxfd2YgJT4lDQogIGxhc3RfZml0KHJmX3NwbGl0KSANCg0KZmluYWxfZml0ICU+JQ0KICBjb2xsZWN0X21ldHJpY3MoKQ0KDQoNCmZpbmFsX2ZpdCAlPiUgICMjIHBsb3QgUk9DIGFuZCBjb21wYXJlIHBlcmZvcm1hbmNlIGFmdGVyIHR1bmluZw0KICBjb2xsZWN0X3ByZWRpY3Rpb25zKCkgJT4lIA0KICByb2NfY3VydmUoZmxvdywgYXZlLnYpICU+JSAjIyMgTk9UIFdPUktJTkcNCiAgYXV0b3Bsb3QoKQ0KDQpmaW5hbF90cmVlIDwtIGV4dHJhY3Rfd29ya2Zsb3coZmluYWxfZml0KSAgIyMgZXh0cmFjdCBvdXIgZmluYWwgZml0IGZvciBmdXR1cmUgdXNlDQpmaW5hbF90cmVlDQoNCmZpbmFsX3RyZWUgJT4lICAjIyBjcmVhdGVzIHdvcmtmbG93IHBsb3QNCiAgZXh0cmFjdF9maXRfZW5naW5lKCkgJT4lDQogIHJwYXJ0LnBsb3Qocm91bmRpbnQgPSBGQUxTRSkNCg0KZmluYWxfdHJlZSAlPiUgIyMgc2hvd3Mgd2hpY2ggdmFyaWFibGVzIGFyZSBtb3N0IGltcG9ydGFudCB0byB0aGUgbW9kZWwgaW4gYSBwbG90DQogIGV4dHJhY3RfZml0X3BhcnNuaXAoKSAlPiUgDQogIHZpcCgpDQoNCmFyZ3MoZGVjaXNpb25fdHJlZSkNCg0KDQpgYGANCkJpZ2dlciBSRiBNb2RlbA0KYGBge3J9DQpjb3JlcyA8LSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSAjIyBzZWVzIGhvdyBtYW55IGNvcmVzIHdlIGhhdmUgdG8gcHJvY2VzcyB0aGUgZGF0YQ0KY29yZXMNCg0KcmZfbW9kIDwtICMjIHJhbmRvbSBmb3Jlc3QgbW9kZWwgZ2VuZXJhdGlvbiwgcGFyYWxsZWwgcHJvY2Vzc2luZyBvZiBtb2RlbHMNCiAgcmFuZF9mb3Jlc3QobXRyeSA9IHR1bmUoKSwgbWluX24gPSB0dW5lKCksIHRyZWVzID0gMTAwMCkgJT4lIA0KICBzZXRfZW5naW5lKCJyYW5nZXIiLCBudW0udGhyZWFkcyA9IGNvcmVzKSAlPiUgDQogIHNldF9tb2RlKCJyZWdyZXNzaW9uIikNCg0KcmZfcmVjaXBlIDwtICMjIGNyZWF0ZSByYW5kb20gZm9yZXN0IG1vZGVsIHJlY2lwZQ0KICByZWNpcGUoYXZlLnYgfiAuLCBkYXRhID0gZGYpIA0KICANCnJmX3dvcmtmbG93IDwtICMjIGNyZWF0ZSByYW5kb20gZm9yZXN0IG1vZGVsIHdvcmtmbG93DQogIHdvcmtmbG93KCkgJT4lIA0KICBhZGRfbW9kZWwocmZfbW9kKSAlPiUgDQogIGFkZF9yZWNpcGUocmZfcmVjaXBlKQ0KDQpyZl9tb2QNCg0KdmFsX3NldCA8LSB2YWxpZGF0aW9uX3NwbGl0KGRmLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJhdGEgPSBmbG93LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9wID0gMC44MCkNCnZhbF9zZXQNCiMgc2hvdyB3aGF0IHdpbGwgYmUgdHVuZWQNCmV4dHJhY3RfcGFyYW1ldGVyX3NldF9kaWFscyhyZl9tb2QpDQoNCnNldC5zZWVkKDM0NSkNCnJmX3JlcyA8LSANCiAgcmZfd29ya2Zsb3cgJT4lIA0KICB0dW5lX2dyaWQodmFsX3NldCwNCiAgICAgICAgICAgIGdyaWQgPSAyNSwNCiAgICAgICAgICAgIGNvbnRyb2wgPSBjb250cm9sX2dyaWQoc2F2ZV9wcmVkID0gVFJVRSksDQogICAgICAgICAgICBtZXRyaWNzID0gbWV0cmljX3NldChybXNlKSkNCg0KcmZfcmVzICU+JSAjIyBzaG93cyA1IGJlc3QgcmFuZG9tIGZvcmVzdCBtb2RlbHMgb3V0IG9mIHRoZSAyNSBjYW5kaWRhdGVzDQogIHNob3dfYmVzdChtZXRyaWMgPSAicm1zZSIpDQoNCmF1dG9wbG90KHJmX3JlcykgICMjIHBsb3QgcmVzdWx0cw0KDQpgYGANCg0KYGBge3J9DQpyZl9iZXN0IDwtICMjIGNyZWF0ZXMgbW9kZWwgd2l0aCBiZXN0IHByZWRpY3RvcnMNCiAgcmZfcmVzICU+JSANCiAgc2VsZWN0X2Jlc3QobWV0cmljID0gInJtc2UiKQ0KcmZfYmVzdA0KDQpyZl9yZXMgJT4lICMjIGNvbGxlY3RzIGRhdGEgZm9yIFJPQyBjdXJ2ZSBwbG90DQogIGNvbGxlY3RfcHJlZGljdGlvbnMoKQ0KDQpyZl9iZXN0JG10cnkgPC0gYXMuaW50ZWdlcihyZl9iZXN0JG10cnkpDQoNCg0KIyNOT1QgV09SS0lORw0KcmZfYXVjIDwtICMjIGNyZWF0ZXMgc2V0IG9mIG1vZGVscyB3aXRoIGJlc3QgbW9kZWwgYW5kIG1vZGVsIG1vZGVsIGZvciBjb21wYXJpc29uDQogIHJmX3JlcyAlPiUgDQogIGNvbGxlY3RfcHJlZGljdGlvbnMocGFyYW1ldGVycyA9IHJmX2Jlc3QpICU+JSANCiAgcm9jX2N1cnZlKG10cnksIC5wcmVkKSAlPiUgDQogIG11dGF0ZShtb2RlbCA9ICJSYW5kb20gRm9yZXN0IikgIA0KDQojI05PVCBXT1JLSU5HDQpiaW5kX3Jvd3MocmZfYmVzdCwgcmZfcmVzKSAlPiUgIyMgcGxvdHMgbW9kZWwgY29tcGFyaXNvbnMgb24gUk9DIGN1cnZlDQogIGdncGxvdChhZXMoeCA9IDEgLSBzcGVjaWZpY2l0eSwgeSA9IHNlbnNpdGl2aXR5LCBjb2wgPSBtb2RlbCkpICsgDQogIGdlb21fcGF0aChsd2QgPSAxLjUsIGFscGhhID0gMC44KSArDQogIGdlb21fYWJsaW5lKGx0eSA9IDMpICsgDQogIGNvb3JkX2VxdWFsKCkgKyANCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKG9wdGlvbiA9ICJwbGFzbWEiLCBlbmQgPSAuNikNCmBgYA0KDQpgYGB7cn0NCiMjIyMjIyMjIyMjIyMjIyMgbGFzdCBtb2RlbCBhZnRlciB0dW5pbmcgDQoNCiMgdGhlIGxhc3QgbW9kZWwNCmxhc3RfcmZfbW9kIDwtIA0KICByYW5kX2ZvcmVzdChtdHJ5ID0gOCwgbWluX24gPSA3LCB0cmVlcyA9IDEwMDApICU+JSANCiAgc2V0X2VuZ2luZSgicmFuZ2VyIiwgbnVtLnRocmVhZHMgPSBjb3JlcywgaW1wb3J0YW5jZSA9ICJpbXB1cml0eSIpICU+JSANCiAgc2V0X21vZGUoInJlZ3Jlc3Npb24iKQ0KDQojIHRoZSBsYXN0IHdvcmtmbG93DQpsYXN0X3JmX3dvcmtmbG93IDwtIA0KICByZl93b3JrZmxvdyAlPiUgDQogIHVwZGF0ZV9tb2RlbChsYXN0X3JmX21vZCkNCg0KIyB0aGUgbGFzdCBmaXQNCnNldC5zZWVkKDM0NSkNCmxhc3RfcmZfZml0IDwtIA0KICBsYXN0X3JmX3dvcmtmbG93ICU+JSANCiAgbGFzdF9maXQocmZfc3BsaXQpDQoNCmxhc3RfcmZfZml0DQoNCmxhc3RfcmZfZml0ICU+JSAgIyMgY29sbGVjdCBtZXRyaWNzIGZyb20gZmluYWwgbW9kZWwNCiAgY29sbGVjdF9tZXRyaWNzKCkNCg0KbGFzdF9yZl9maXQgJT4lICMjIHVwZGF0ZXMgbW9kZWwgZml0DQogIGV4dHJhY3RfZml0X3BhcnNuaXAoKSAlPiUgDQogIHZpcChudW1fZmVhdHVyZXMgPSAyMCkNCg0KIyNOT1QgV09SS0lORw0KbGFzdF9yZl9maXQgJT4lICMjIHBsb3RzIGJlc3QgUk9DIGN1cnZlLCB3aXRoIGJlc3Qgc2V0IG9mIGh5cGVycGFyYW1ldGVycyBhcyBwcmVkaWN0b3JzDQogIGNvbGxlY3RfcHJlZGljdGlvbnMoKSAlPiUgDQogIHJvY19jdXJ2ZShhdmUudiwgLnByZWQuLi4xKSAlPiUgDQogIGF1dG9wbG90KCkNCg0KYGBgDQoNCg==